/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "gmock/gmock-actions.h"
#include "mockcpp/mockcpp.hpp"
#include "NodeTaskManager.h"
#include "PrefixUtils.h"
using namespace OBSPlugin;

class NodeTaskManagerTest : public testing::Test {
public:
    void SetUp() {};
    void TearDown()
    {
        GlobalMockObject::verify();
    };
};

/*
 * 用例名称：获取新增和删除的前缀信息
 * 前置条件：无
 * check点：检查前后两次扫描的前缀信息
 */
TEST_F(NodeTaskManagerTest, ComputeDifferencePrefix)
{
    const std::vector<PrefixInfo> lastPrefix {
        {"bucket1", "/opt/aa", "11/22", false},
        {"bucket1", "/opt/aa", "11/33", false},
        {"bucket2", "/opt/bb", "/33/44", false},
        {"bucket2", "/opt/bb", "/33/55", false}
    };

    const std::vector<PrefixInfo> curPrefix {
        {"bucket1", "/opt/aa", "11/22", false},
        {"bucket1", "/opt/aa", "11/33", false},
        {"bucket1", "/opt/aa", "11/test11", false},
        {"bucket2", "/opt/bb", "/33/44", false},
        {"bucket2", "/opt/bb", "/test2/test22", false}
    };
    NodePrefixEntity curNodePrefixEntity(curPrefix);
    NodePrefixEntity lastNodePrefixEntity(lastPrefix);
    NodePrefixEntity newNodePrefixEntity = curNodePrefixEntity - lastNodePrefixEntity;
    int expectedCount = 2;
    EXPECT_EQ(newNodePrefixEntity.GetPrefixInfo().size(), expectedCount);

    NodePrefixEntity delNodePrefixEntity = lastNodePrefixEntity - curNodePrefixEntity;
    EXPECT_EQ(delNodePrefixEntity.GetPrefixInfo().size(), 1);
}

/*
 * 用例名称：获取两次前缀的合集
 * 前置条件：无
 * check点：检查前后两次合集的结果是否为叠加
 */
TEST_F(NodeTaskManagerTest, ComputeCombinePrefix)
{
    std::vector<PrefixInfo> lastPrefix {
        {"bucket1", "/home/aa", "11/22", false},
        {"bucket1", "/home/aa", "11/33", false},
        {"bucket2", "/home/bb", "/33/44", false},
        {"bucket2", "/home/bb", "/33/55", false}
    };
    std::vector<PrefixInfo> curPrefix {
        {"bucket1", "/home/aa", "11/22", false},
        {"bucket1", "/home/aa", "11/33", false},
        {"bucket1", "/home/aa", "11/44", false},
        {"bucket2", "/home/bb", "/33/44", false},
    };
    int expectedCount = 5;
    NodePrefixEntity curNodePrefixEntity(curPrefix);
    NodePrefixEntity lastNodePrefixEntity(lastPrefix);
    NodePrefixEntity combineNodePrefixEntity = curNodePrefixEntity + lastNodePrefixEntity;
    EXPECT_EQ(combineNodePrefixEntity.GetPrefixInfo().size(), expectedCount);
}

/*
 * 用例名称：合并相同bucket的前缀
 * 前置条件：无
 * check点：检查合并后的值是否为预期结果
 */
TEST_F(NodeTaskManagerTest, MergeCollectionSuccess)
{
    const std::vector<PrefixInfo> curPrefix{
            {"bucket1", "/opt/aa", "11/22", true},
            {"bucket1", "/opt/aa", "11/33", true}
    };
    auto r = MergeCollection(curPrefix);
    int expectPrefix = 2;
    EXPECT_EQ(r[0].subPrefixs.size(), expectPrefix);
}

/*
 * 用例名称：前缀实体迭代器测试
 * 前置条件：无
 * check点：检查迭代器是否正常统计数据。
 */
TEST_F(NodeTaskManagerTest, PrefixEntityIteratorSUCCESS)
{
    const std::vector<PrefixInfo> curPrefix{
            {"bucket1", "/opt/aa", "11/22", true},
            {"bucket2", "/opt/aa", "11/33", true}
    };
    NodePrefixEntity curNodePrefixEntity(curPrefix);
    PrefixEntityIterator currIt(&curNodePrefixEntity);
    int curPos = 0;
    int expectedSize = 2;
    PrefixInfo lastPrefixInfo;
    while (!currIt.End()) {
        curPos++;
        lastPrefixInfo = currIt.Current();
        currIt++;
    }
    EXPECT_EQ(currIt.Empty(), false);
    EXPECT_EQ(lastPrefixInfo.bucketName, "bucket2");
    EXPECT_EQ(curPos, expectedSize);
    EXPECT_EQ(curNodePrefixEntity.EntityCount(), expectedSize);
}

/*
 * 用例名称：删除指定的前缀实体
 * 前置条件：无
 * check点：检查是否正确删除实体。
 */
TEST_F(NodeTaskManagerTest, RemoveNodePrefixEntitySUCCESS)
{
    std::vector<PrefixInfo> curPrefix{
            {"bucket1", "/opt/aa", {}, true},
            {"bucket2", "/opt/aa", {"11/44"}, false}
    };
    curPrefix[1].subPrefixs.push_back("11/33");
    NodePrefixEntity curNodePrefixEntity(curPrefix);

    const std::vector<PrefixInfo> removePrefix{
            {"bucket1", "/opt/aa", "", true},
            {"bucket2", "/opt/aa", {"11/33"}, false}
    };
    NodePrefixEntity toRemovePrefix;
    toRemovePrefix.Clear();
    toRemovePrefix.AddPrefixInfoList(removePrefix);
    curNodePrefixEntity.Remove(toRemovePrefix);
    auto prefixResult = curNodePrefixEntity.GetPrefixInfo();
    EXPECT_EQ(prefixResult.size(), 1);
    EXPECT_EQ(prefixResult[0].subPrefixs.size(), 1);
    EXPECT_EQ(prefixResult[0].subPrefixs[0], "11/44");
}

/*
 * 用例名称：按照前缀个数，节点个数平均拆分前缀
 * 前置条件：无
 * check点：检查拆分的数量是否正确
 */
TEST_F(NodeTaskManagerTest, AverageAssignSUCCESS)
{
    NodeTaskManager nodeTaskManager;
    uint32_t prefixCount = 5;
    uint32_t nodeCount = 2;
    uint32_t expectSize = 2;
    std::vector<uint32_t> avarageAssign = nodeTaskManager.AverageAssign(prefixCount, nodeCount);
    EXPECT_EQ(avarageAssign.size(), expectSize);
    uint32_t expectedPrefixCount = 3;
    EXPECT_EQ(avarageAssign[1], expectedPrefixCount);

    avarageAssign = nodeTaskManager.AverageAssign(0, nodeCount);
    EXPECT_EQ(avarageAssign.size(), 0);

    avarageAssign = nodeTaskManager.AverageAssign(prefixCount, 0);
    EXPECT_EQ(avarageAssign.size(), 0);

    avarageAssign = nodeTaskManager.AverageAssign(1, nodeCount);
    EXPECT_EQ(avarageAssign.size(), 1);
}

/*
 * 用例名称：将备份对象信息转换为前缀信息实体
 * 前置条件：无
 * check点：检查转换之后的实现数量是否正确
 */
TEST_F(NodeTaskManagerTest, MergeNodePrefixSUCCESS)
{
    std::vector<PrefixInfo> prefix_esn1 {
            {"bucket1", "/opt/aa", {}, true},
            {"bucket2", "/opt/aa", {"11/44"}, false}
    };
    std::vector<PrefixInfo> prefix_esn2 {
            {"bucket2", "/opt/bb", {}, true},
            {"bucket4", "/home/aa", {"test1/test2"}, false},
            {"bucket3", "/home/aa", {"test1/test2"}, false}
    };
    std::vector<BackupObjectInfo> objectInfos;
    BackupObjectInfo obj1;
    obj1.esn = "esn1";
    obj1.prefixInfo = std::move(prefix_esn1);
    objectInfos.emplace_back(obj1);

    BackupObjectInfo obj2;
    obj2.esn = "esn2";
    obj2.prefixInfo = std::move(prefix_esn2);
    objectInfos.emplace_back(obj2);
    int expectSize = 5;
    NodeTaskManager nodeTaskManager;
    NodePrefixEntity nodePrefixEntity = nodeTaskManager.MergeNodePrefix(objectInfos);
    EXPECT_EQ(nodePrefixEntity.EntityCount(), expectSize);
}


/*
 * 用例名称：全量备份首次分配前缀信息
 * 前置条件：无
 * check点：检查分配的前缀信息是否正确
 */
TEST_F(NodeTaskManagerTest, AllocateNodePrefix_FirstAllocateSUCCESS)
{
    std::vector<PrefixInfo> curPrefix{
            {"bucket1", "/opt/aa", {}, true},
            {"bucket3", "/home/aa", {"test1/test2"}, false},
            {"bucket2", "/opt/aa", {"11/44"}, false}
    };
    int i = 0;
    curPrefix[++i].subPrefixs.push_back("test3/test4");
    curPrefix[++i].subPrefixs.push_back("11/33");
    const std::vector<std::string> repoEsnList = {"esn2", "esn1"};
    const std::vector<std::string> emptyEsnList = {};
    NodeTaskManager nodeTaskManager;
    std::vector<BackupObjectInfo> lastObjectInfo;
    auto allocateResult = nodeTaskManager.AllocateNodePrefix(curPrefix,
        emptyEsnList, lastObjectInfo);
    EXPECT_EQ(allocateResult.size(), 0);
    allocateResult = nodeTaskManager.AllocateNodePrefix(curPrefix,
        repoEsnList, lastObjectInfo);
    int expectedAllocateSize = 2;
    EXPECT_EQ(allocateResult.size(), expectedAllocateSize);
}

/*
 * 用例名称：全量备份首次分配前缀信息
 * 前置条件：无
 * check点：检查分配的前缀信息是否正确
 */
TEST_F(NodeTaskManagerTest, AllocateNodePrefix_SecondAllocateSUCCESS)
{
    // 构造上一次节点分配信息
    std::vector<PrefixInfo> prefix_esn1 {
            {"bucket1", "/opt/aa", {}, true},
            {"bucket2", "/opt/aa", {"11/44"}, false}
    };
    std::vector<PrefixInfo> prefix_esn2 {
            {"bucket2", "/opt/bb", {}, true},
            {"bucket4", "/home/aa", {"test1/test2"}, false},
            {"bucket3", "/home/aa", {"test1/test2"}, false}
    };
    prefix_esn2[1].subPrefixs.push_back("test3/test4");
    std::vector<BackupObjectInfo> prevObjectInfos;
    BackupObjectInfo obj1;
    obj1.esn = "esn1";
    obj1.prefixInfo = std::move(prefix_esn1);
    prevObjectInfos.emplace_back(obj1);

    BackupObjectInfo obj2;
    obj2.esn = "esn2";
    obj2.prefixInfo = std::move(prefix_esn2);
    prevObjectInfos.emplace_back(obj2);

    // 构造本次扫描结果
    std::vector<PrefixInfo> curPrefix{
            {"bucket1", "/opt/aa", {}, true},
            {"bucket2", "/opt/aa", {"11/44"}, false},
            {"bucket2", "/opt/bb", {}, true},
            {"bucket4", "/home/aa", {"test3/test4"}, false},
            {"bucket3", "/home/aa", {"test1/test2"}, false}
    };

    curPrefix[1].subPrefixs.push_back("11/33");
    const std::vector<std::string> repoEsnList = {"esn2", "esn1"};

    NodeTaskManager nodeTaskManager;
    auto allocateResult = nodeTaskManager.AllocateNodePrefix(curPrefix,
        repoEsnList, prevObjectInfos);
    int expectedAllocateSize = 2;
    EXPECT_EQ(allocateResult.size(), expectedAllocateSize);
    EXPECT_EQ(allocateResult[0].prefixInfo.size(), expectedAllocateSize);
}
